package com.xixibaby.core.service;

import cn.hutool.core.convert.Convert;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xixibaby.core.*;
import com.xixibaby.core.Assert;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;


import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author tomj
 */
@Slf4j
public abstract class SuperServiceImpl<
                MAPPER extends SuperMapper<PO>,
                PO extends SuperPO,
                RESPONSE extends SuperResponse,
                REQUEST extends SuperRequest,
                CONCATER extends SuperConverter<PO,RESPONSE,REQUEST>>
        extends ServiceImpl<MAPPER,PO> implements IService<PO>, SuperService<PO,RESPONSE,REQUEST> {


    @Autowired
    protected CONCATER concater;

    /**
     * 过程函数
     * @param request
     */
    protected void beforeInsert(REQUEST request){}
    protected void afterInsert(REQUEST request,PO newEntity){}
    protected void afterUpdate(REQUEST request,PO updateEntity){}
    protected void beforeUpdate(REQUEST request){}
    protected void afterDelete(REQUEST request,PO deleteEntity){}
    protected void beforeDelete(REQUEST request){}
    protected void afterSelectById(RESPONSE entity){}

    public abstract Wrapper<PO> getQueryWrapper(REQUEST vo);



    private RESPONSE afterSelectByIdFromPO(PO entity){
        RESPONSE response = concater.toResult(entity);
        this.afterSelectById(response);
        return response;
    }

    protected void afterPageList(RESPONSE response){}

    private RESPONSE afterPageListFromPO(PO entity){
        RESPONSE response = concater.toResult(entity);
        this.afterPageList(response);
        return response;
    }

    protected void afterList(RESPONSE entity){}



    /**数据库操作**/
    protected abstract IPage<PO> pageListDb(REQUEST vo);
    protected abstract IPage<PO> pageListDb(REQUEST vo,Function<REQUEST,Wrapper<PO>> function);

    protected abstract List<PO> listDb(REQUEST vo);

    protected abstract PO selectByIdDb(REQUEST vo);
    protected abstract PO insertDb(REQUEST vo);
    protected abstract PO updateDb(REQUEST vo);
    protected abstract PO deleteDb(REQUEST vo);


    /**缓存操作
     * @param request**/
    protected abstract PO selectCacheById(REQUEST request);
    protected abstract void insertCache(PO po);
    protected abstract void updateCache(PO po);
    protected abstract void deleteCache(PO po);

    /**
     * 获取更新后的最新信息  用于保存操作日志
     * @param entity 注意这儿最好是刚从数据库拿出来的数据  也就是说  调用这个方法之前  先调用getById
     * @return
     */
    public abstract RESPONSE getLastInfo(PO entity);


    protected void deleteValid(REQUEST request){
        Assert.succ(request.getId()).thenThrow("ID不能为空！");
        Assert.succ(request.getVersion()).thenThrow("版本号不能为空！");
    }


    protected void updateValid(REQUEST request){
        Assert.succ(request.getId()).thenThrow("ID不能为空！");
        Assert.succ(request.getVersion()).thenThrow("版本号不能为空！");
    }



    public void insertValid(REQUEST request) { }

    /**
     * deleteValid -> beforeDelete-> getLastInfo -> doDelete -> afterDelete(lastInfo) -> deleteCache(lastInfo)
     * @param request
     * @return
     */
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public RESPONSE removeById(REQUEST request){
        //验证是否允许删除
        return this.removeById(request,this::deleteValid);
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public RESPONSE removeById(REQUEST request, Consumer<REQUEST> valid) {
        valid.accept(request);
        beforeDelete(request);
        PO po = deleteDb(request);
        afterDelete(request,po);
        deleteCache(po);
        return concater.toResult(po);
    }

    /**
     * 模板顺序  insertValid -> beforeInsert -> doInsert -> afterInsert -> getLastInfo -> insertCache
     * @param request
     * @return
     */

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public  RESPONSE insert(REQUEST request) {
        return this.insert(request, this::getLastInfo);
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public RESPONSE insert(REQUEST request, Consumer<REQUEST> valid,Function<PO,RESPONSE> converter) {
        valid.accept(request);
        beforeInsert(request);
        PO po = insertDb(request);
        afterInsert(request,po);
        RESPONSE lastInfo = converter.apply(po);
        insertCache(po);
        return lastInfo;
    }

    /**
     * 自定义数据验证
     * @param request
     * @param valid
     * @return
     */
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public RESPONSE insert(REQUEST request, Consumer<REQUEST> valid) {
        return this.insert(request, valid, this.concater::toResult);
    }

    /**
     * 自定义 返回
     * @param request
     * @param converter
     * @return
     */
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public RESPONSE insert(REQUEST request, Function<PO,RESPONSE> converter) {
        return this.insert(request,this::insertValid, converter);
    }

    /**
     * 模板顺序  updateValid -> beforeUpdate -> doUpdate ->afterUpdate -> getLastInfo -> updateCache
     * @param request
     * @return
     */
    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public RESPONSE updateById(REQUEST request) {
        return this.updateById(request,this::beforeUpdate,this::afterUpdate,this::updateValid);
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public RESPONSE updateById(REQUEST request, Consumer<REQUEST> valid) {
        return this.updateById(request,this::beforeUpdate,this::afterUpdate,valid);
    }

    @Transactional(rollbackFor = RuntimeException.class)
    public RESPONSE updateById(REQUEST request, Consumer<REQUEST> before, BiConsumer<REQUEST,PO> after,Consumer<REQUEST> valid) {
        valid.accept(request);
        before.accept(request);
        PO po = updateDb(request);
        after.accept(request,po);
        RESPONSE lastInfo = getLastInfo(po);
        updateCache(po);
        return lastInfo;
    }

    /**
     * pageListCache -> pageListDB
     * @param request
     * @return
     */
    @Override
    public IPage<RESPONSE> pageList(REQUEST request) {
        return pageList(request, this::afterPageListFromPO);
    }

    @Override
    public IPage<RESPONSE> pageList(Function<REQUEST, Wrapper<PO>> function, REQUEST request) {
        return pageList(request,this::afterPageListFromPO,function);
    }


    @Override
    public <T extends SuperResponse> IPage<T> pageList(REQUEST request, Function<PO, T> after) {
        return pageList(request, after, this::getQueryWrapper);
    }

    @Override
    public <T extends SuperResponse> IPage<T> pageList(REQUEST request, Function<PO, T> after,Function<REQUEST,Wrapper<PO>> function) {
        IPage<PO> poiPage = pageListDb(request,function);
        List<T> collect = poiPage.getRecords().stream().map(after).collect(Collectors.toList());
        IPage<T> response = new Page<>(poiPage.getCurrent(),poiPage.getSize(),poiPage.getTotal());
        response.setRecords(collect);
        return response;
    }

    @Override
    public List<RESPONSE> list(REQUEST request) {
        List<PO> lists = listDb(request);
        List<RESPONSE> collect = lists.stream().map(concater::toResult)
                .peek(this::afterList)
                .collect(Collectors.toList());
        return collect;
    }



    @Override
    public RESPONSE selectById(Long id) {
        return this.selectById(id,this::afterSelectByIdFromPO);
    }

    @Override
    public RESPONSE selectById(Long id, boolean throwExp) {
        return this.selectById(id,this::afterSelectByIdFromPO, throwExp);
    }

    /**
     * selectByIdCache -> selectByIdDb -> afterSelectById
     * @param request
     * @return
     */
    @Override
    public RESPONSE selectById(REQUEST request) {
        return selectById(request.getId());
    }

    @Override
    public <T extends SuperResponse> T selectById(Long id, Function<PO, T> afterSelect) {
        return this.selectById(id,afterSelect,false);
    }

    @Override
    public PO getById(Serializable id) {
        ParameterizedType genericSuperclass = (ParameterizedType)this.getClass().getGenericSuperclass();
        Type[] actualTypeArguments = genericSuperclass.getActualTypeArguments();
        Class<REQUEST> requestClass = (Class<REQUEST>) actualTypeArguments[3];
        SuperRequest request = new SuperRequest();
        log.info("根据id查PO,ID[{}],PO[{}]",id,requestClass.getSimpleName());
        request.setId(Long.valueOf(id.toString()));

        REQUEST req = Convert.convert(requestClass,request);
        PO po1 = this.selectCacheById(req);
        if(Assert.empty(po1)){
            po1 = this.selectByIdDb(req);
            if(Assert.notEmpty(po1)){
                this.insertCache(po1);
            }
        }
        return po1;
    }

    @Override
    public <T extends SuperResponse> T selectById(Long id, Function<PO, T> afterSelect, boolean throwExp) {
        Assert.succ(id).thenThrow("id不能为空！");
        PO byId = this.getById(id);
        if(Assert.empty(byId) && throwExp){
            Assert.succ(true).thenThrow("当前【{}】【{}】数据不存在",byId.getClass().getSimpleName(),id);
        }else{
            Assert.succ(true).thenLogError("【"+id+"】数据不存在");
        }
        return afterSelect.apply(byId);
    }


    @Override
    public <T extends SuperResponse> T selectById(REQUEST request, Function<PO, T> afterSelect) {
        return this.selectById(request.getId(),afterSelect);
    }

    @Override
    public RESPONSE selectOneByPro(String proName, Object value) {
        return concater.toResult(selectOnePo(proName,value));
    }

    @Override
    public RESPONSE selectOneByPro(SFunction<PO,Object> proName, Object value) {
        return concater.toResult(selectOnePo(proName,value));
    }

    @Override
    public PO selectOnePo(List<String> proNames, Object... value) {
        Assert.succ(proNames.size() == value.length).thenThrow("查询参数与值个数不匹配！");
        QueryWrapper<PO> query = Wrappers.query();
        for (int i = 0; i < proNames.size(); i++) {
            query.eq(proNames.get(i),value[i]);
        }
        return this.getOne(query);
    }

    @Override
    public PO selectOnePo(String proName,Object value) {
        return this.getOne(Wrappers.<PO>query().eq(proName, value));
    }

    @Override
    public PO selectOnePo(SFunction<PO, Object> proName, Object value) {
        LambdaQueryWrapper<PO> lambdaQueryWrapper = Wrappers.<PO>query().lambda().eq(proName, value);
        return this.getOne(lambdaQueryWrapper);
    }
}
